﻿using System;
using System.Runtime.Remoting.Messaging; //AsyncResult

namespace Bettzueche_CCR.Benchmarks {
    /// <summary>
    /// EventHandler für Ereignis, dass nach Beendigung einer Berechnung ausgelöst wird
    /// </summary>
    /// <param name="e"></param>
    delegate void FinishedComputationEventHandler(FinishedComputationArgs e);

    /// <summary>
    /// EventArgs für FinishedComputationEventHandler enthält eine Überschrift.
    /// </summary>
    class FinishedComputationArgs : EventArgs {
        public string Header { get; set; }

        public FinishedComputationArgs()
            : base() {
        }
    }

    /// <summary>
    /// Delegat zur asynchronen Ausführung
    /// </summary>
    /// <param name="param">double</param>
    /// <returns>int</returns>
    public delegate int asyncCompute(double param);




    /// <summary>
    /// Asynchronous Programming Model, benutzt Standard Features von C#.
    /// Der Test:
    /// <para>
    /// Es werden N (prop Repetitions) asynchrone Methodenaufrufe getätigt. Jeder Aufruf beinhaltet ein Cast von double zu int
    /// und 100.000 Additionen. Die Ergebnisse werden durch Callbacks aufsummiert.
    /// </para>
    /// </summary>
    class APM : CallBackTester {

        internal event FinishedComputationEventHandler FinishedComputation;

        int totalSum;
        object _locker = new object();

        /// <summary>
        /// Default Instanz mit Repitions=1000 und LogFile="APM_Benchmark.log" im
        /// Assembly-Pfad.
        /// </summary>
        public APM()
            : this(1000) {
        }

        /// <summary>
        /// Instanz mit der angegebenen Anzahl von Repitions und LogFile="APM_Benchmark.log" im
        /// Assembly-Pfad.
        /// </summary>
        public APM(int repititions) {
            Repititions = repititions;
            LogFile = "APM_Benchmark.log";
        }

        /// <summary>
        /// Castet den Input zu int und addiert 100.000 mal abwechselnd +/-1.
        /// </summary>
        /// <param name="input">nach int zu castende Zahl</param>
        /// <returns>(int)input, falls Repititions gerade, (int)input -1 sonst.</returns>
        int asyncComputation(double input) {
            int result = (int)input;
            //doing havy computation ;-)
            for (int i = 0; i < 100000; i++) {
                result += (int)Math.Pow(-1.0, i);
            }
            return result;
        }


        /// <summary>
        /// Callback für einen asynchronen Aufruf von asyncComputation.
        /// </summary>
        /// <param name="ar">AsyncResult eines asyncCompute Delegaten</param>
        void asyncSumCallback(IAsyncResult ar) {
            asyncCompute aDel = (asyncCompute)((AsyncResult)ar).AsyncDelegate;
            int result = aDel.EndInvoke(ar);
            int loopCount = (int)ar.AsyncState;
            lock (_locker) {
                totalSum += result;
            }
            if (loopCount == (this.Repititions - 1)) {
                this._endTime = DateTime.Now;
                FinishedComputation(new FinishedComputationArgs { Header = "Standard APM" });
            }
        }

        /// <summary>
        /// Schreibt Log-Infos zur Berechnungsdauer und Systeminformationen in die in LogFile angegebene Datei.
        /// </summary>
        /// <param name="e">Argument mit Info zur Überschrift</param>
        protected void APM_FinishedComputation(FinishedComputationArgs e) {
            //calculate duration of async call
            TimeSpan time = _endTime - _startTime;
            Utilities.WriteLog(LogFile, Repititions, _startTime, time.TotalMilliseconds, e.Header);
            computing = false;
        }


        internal void Start() {
            asyncCompute aDel = asyncComputation;
            FinishedComputation += APM_FinishedComputation;
            computing = true;
            _startTime = DateTime.Now;
            for (int i = 0; i < this.Repititions; i++) {
                aDel.BeginInvoke(3.14, asyncSumCallback, i);
            }
            this.doSomeStuff();
            Console.WriteLine("--- End of APM.Start(), ---");
        }
    }
}
